import os
import subprocess
import platform
import tempfile
import base64
from PyQt5.QtCore import QThread, pyqtSignal
from icon_data import get_mark_data

# 添加硬件加速选项常量
HW_ACCEL_NONE = "cpu (不使用硬件加速)"
HW_ACCEL_NVIDIA = "nvidia (NVIDIA GPU加速)"
HW_ACCEL_AMD = "amd (AMD GPU加速)"
HW_ACCEL_INTEL = "qsv (Intel Quick Sync加速)"

class VideoProcessingThread(QThread):
    progress_update = pyqtSignal(int, int)
    processing_file = pyqtSignal(str)
    finished_processing = pyqtSignal()
    error_occurred = pyqtSignal(str)
    log_message = pyqtSignal(str)  # 添加日志信号

    def __init__(self, ffmpeg_path, input_files, time_offset, add_black_line=False, black_line_width=1, 
                 add_watermark=False, watermark_size=1.0, hw_accel="cpu"):
        super().__init__()
        self.ffmpeg_path = ffmpeg_path
        self.input_files = input_files
        self.time_offset = time_offset
        self.is_running = True
        self.add_black_line = add_black_line
        self.black_line_width = black_line_width
        self.add_watermark = add_watermark
        self.watermark_size = watermark_size  # 添加水印尺寸倍数
        self.watermark_path = None
        self.current_process = None
        self.hw_accel = hw_accel  # 硬件加速选项
    
    def log(self, message):
        """发送日志消息"""
        print(message)  # 保留控制台输出
        self.log_message.emit(message)  # 发送到UI
    
    def run(self):
        total_files = len(self.input_files)
        
        # 准备水印图片
        if self.add_watermark:
            try:
                watermark_data = get_mark_data()
                fd, self.watermark_path = tempfile.mkstemp(suffix='.png')
                os.close(fd)
                with open(self.watermark_path, 'wb') as f:
                    f.write(base64.b64decode(watermark_data))
                self.processing_file.emit("已准备水印图片")
                self.log("已准备水印图片")
            except Exception as e:
                error_msg = f"准备水印图片时出错: {str(e)}"
                self.log(error_msg)
                self.error_occurred.emit(error_msg)
                self.finished_processing.emit()  # 确保在出错时也发送完成信号
                return
        
        # 保存原始硬件加速设置，用于在失败时回退
        original_hw_accel = self.hw_accel
        hw_accel_failed = False
        
        for i, input_file in enumerate(self.input_files):
            if not self.is_running:
                break
                
            try:
                file_msg = f"处理文件: {os.path.basename(input_file)}"
                self.processing_file.emit(file_msg)
                self.log(file_msg)
                
                # 如果之前的硬件加速失败，使用CPU模式
                if hw_accel_failed:
                    self.hw_accel = "cpu"
                    hw_msg = "硬件加速失败，已切换到CPU模式"
                    self.processing_file.emit(hw_msg)
                    self.log(hw_msg)
                
                file_name, file_ext = os.path.splitext(input_file)
                output_file = f"{file_name}_3D{file_ext}"
                temp_file = f"{file_name}_temp{file_ext}"
                watermarked_file = f"{file_name}_watermarked{file_ext}"
                
                # 处理水印
                # 如果需要添加水印，先处理水印
                if self.add_watermark and self.watermark_path:
                    self.processing_file.emit(f"添加水印中...")
                    self.log("添加水印中...")
                    try:
                        # 构建水印命令，添加硬件加速
                        watermark_cmd = []
                        
                        # 添加硬件加速参数（在输入文件之前）
                        if self.hw_accel == "nvidia":
                            watermark_cmd.extend([self.ffmpeg_path, "-hwaccel", "cuda", "-hwaccel_output_format", "cuda"])
                        elif self.hw_accel == "amd":
                            watermark_cmd.extend([self.ffmpeg_path, "-hwaccel", "d3d11va"])
                        elif self.hw_accel == "qsv":
                            watermark_cmd.extend([self.ffmpeg_path, "-hwaccel", "qsv", "-hwaccel_output_format", "qsv"])
                        else:  # cpu
                            watermark_cmd.extend([self.ffmpeg_path])
                        
                        # 添加输入文件
                        watermark_cmd.extend([
                            "-i", input_file,
                            "-i", self.watermark_path
                        ])
                        
                        # 添加滤镜和编码器
                        scale_filter = f"scale=iw*{self.watermark_size}:ih*{self.watermark_size}"
                        filter_complex = f"[1:v]{scale_filter}[watermark];[0:v][watermark]overlay=(W-w)/2:(H-h-10)"
                        
                        if self.hw_accel == "nvidia":
                            watermark_cmd.extend([
                                "-filter_complex", filter_complex,
                                "-c:v", "h264_nvenc",
                                "-c:a", "copy"
                            ])
                        elif self.hw_accel == "amd":
                            watermark_cmd.extend([
                                "-filter_complex", filter_complex,
                                "-c:v", "h264_amf",
                                "-c:a", "copy"
                            ])
                        elif self.hw_accel == "qsv":
                            watermark_cmd.extend([
                                "-filter_complex", filter_complex,
                                "-c:v", "h264_qsv",
                                "-c:a", "copy"
                            ])
                        else:  # cpu
                            watermark_cmd.extend([
                                "-filter_complex", filter_complex,
                                "-c:v", "libx264",
                                "-c:a", "copy"
                            ])
                        
                        # 输出文件
                        watermark_cmd.extend(["-y", watermarked_file])
                        
                        # 输出命令行
                        cmd_str = " ".join(watermark_cmd)
                        self.log(f"执行水印命令: {cmd_str}")
                        self.processing_file.emit(f"执行水印命令...")
                        
                        # 执行命令并捕获输出，隐藏窗口
                        startupinfo = None
                        if platform.system() == "Windows":
                            startupinfo = subprocess.STARTUPINFO()
                            startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
                            startupinfo.wShowWindow = subprocess.SW_HIDE
                        
                        process = subprocess.run(watermark_cmd, check=True, stderr=subprocess.PIPE, 
                                               stdout=subprocess.PIPE, text=True, startupinfo=startupinfo)
                        self.log(f"水印命令输出: {process.stderr}")
                        
                        # 使用添加了水印的文件作为输入
                        processing_input = watermarked_file
                    except Exception as e:
                        error_msg = f"添加水印时出错: {str(e)}"
                        self.log(error_msg)
                        
                        # 如果是硬件加速模式失败，尝试使用CPU模式重试
                        if self.hw_accel != "cpu":
                            cpu_msg = "硬件加速添加水印失败，尝试使用CPU模式..."
                            self.processing_file.emit(cpu_msg)
                            self.log(cpu_msg)
                            hw_accel_failed = True
                            try:
                                # 使用CPU模式重试
                                cpu_watermark_cmd = [
                                    self.ffmpeg_path,
                                    "-i", input_file,
                                    "-i", self.watermark_path,
                                    "-filter_complex", filter_complex,
                                    "-c:v", "libx264",
                                    "-c:a", "copy",
                                    "-y", watermarked_file
                                ]
                                
                                cmd_str = " ".join(cpu_watermark_cmd)
                                self.log(f"使用CPU模式执行水印命令: {cmd_str}")
                                
                                # 隐藏窗口
                                startupinfo = None
                                if platform.system() == "Windows":
                                    startupinfo = subprocess.STARTUPINFO()
                                    startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
                                    startupinfo.wShowWindow = subprocess.SW_HIDE
                                
                                process = subprocess.run(cpu_watermark_cmd, check=True, stderr=subprocess.PIPE, 
                                                      stdout=subprocess.PIPE, text=True, startupinfo=startupinfo)
                                self.log(f"CPU水印命令输出: {process.stderr}")
                                
                                # 使用添加了水印的文件作为输入
                                processing_input = watermarked_file
                                success_msg = "使用CPU模式添加水印成功"
                                self.processing_file.emit(success_msg)
                                self.log(success_msg)
                            except Exception as cpu_e:
                                self.log(f"CPU模式添加水印也失败: {str(cpu_e)}")
                                # 如果CPU模式也失败，继续使用原始文件
                                processing_input = input_file
                                fallback_msg = "水印添加失败，将使用原始文件继续"
                                self.processing_file.emit(fallback_msg)
                                self.log(fallback_msg)
                        else:
                            # 如果是CPU模式失败，直接使用原始文件
                            processing_input = input_file
                            fallback_msg = "水印添加失败，将使用原始文件继续"
                            self.processing_file.emit(fallback_msg)
                            self.log(fallback_msg)
                else:
                    # 不添加水印，直接使用原始文件
                    processing_input = input_file
                
                # 步骤1: 裁剪视频
                try:
                    # 构建裁剪命令，添加硬件加速
                    cmd1 = []
                    
                    # 添加硬件加速参数（在输入文件之前）
                    if self.hw_accel == "nvidia":
                        cmd1.extend([self.ffmpeg_path, "-hwaccel", "cuda", "-hwaccel_output_format", "cuda"])
                    elif self.hw_accel == "amd":
                        cmd1.extend([self.ffmpeg_path, "-hwaccel", "d3d11va"])
                    elif self.hw_accel == "qsv":
                        cmd1.extend([self.ffmpeg_path, "-hwaccel", "qsv", "-hwaccel_output_format", "qsv"])
                    else:  # cpu
                        cmd1.extend([self.ffmpeg_path])
                    
                    # 添加时间偏移和输入文件
                    cmd1.extend([
                        "-ss", f"00:00:00.{self.time_offset:03d}",
                        "-i", processing_input
                    ])
                    
                    # 添加编码器参数
                    if self.hw_accel == "nvidia":
                        cmd1.extend(["-c:v", "h264_nvenc", "-c:a", "copy"])
                    elif self.hw_accel == "amd":
                        cmd1.extend(["-c:v", "h264_amf", "-c:a", "copy"])
                    elif self.hw_accel == "qsv":
                        cmd1.extend(["-c:v", "h264_qsv", "-c:a", "copy"])
                    else:  # cpu
                        cmd1.extend(["-vcodec", "copy", "-acodec", "copy"])
                    
                    # 输出文件
                    cmd1.extend(["-y", temp_file])
                    
                    # 输出命令行
                    cmd_str = " ".join(cmd1)
                    self.log(f"执行裁剪命令: {cmd_str}")
                    self.processing_file.emit(f"执行裁剪命令...")
                    
                    # 隐藏窗口
                    startupinfo = None
                    if platform.system() == "Windows":
                        startupinfo = subprocess.STARTUPINFO()
                        startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
                        startupinfo.wShowWindow = subprocess.SW_HIDE
                    
                    self.current_process = subprocess.Popen(cmd1, stderr=subprocess.PIPE, stdout=subprocess.PIPE, 
                                                          text=True, startupinfo=startupinfo)
                    stdout, stderr = self.current_process.communicate()
                    self.log(f"裁剪命令输出: {stderr}")
                    
                    if self.current_process.returncode != 0:
                        raise subprocess.CalledProcessError(self.current_process.returncode, cmd1, stderr)
                except Exception as e:
                    # 如果硬件加速模式失败，尝试使用CPU模式
                    if self.hw_accel != "cpu":
                        cpu_msg = "硬件加速裁剪失败，尝试使用CPU模式..."
                        self.processing_file.emit(cpu_msg)
                        self.log(cpu_msg)
                        hw_accel_failed = True
                        
                        # 使用CPU模式重试
                        cpu_cmd1 = [
                            self.ffmpeg_path,
                            "-ss", f"00:00:00.{self.time_offset:03d}",
                            "-i", processing_input,
                            "-vcodec", "copy", "-acodec", "copy",
                            "-y", temp_file
                        ]
                        
                        cmd_str = " ".join(cpu_cmd1)
                        self.log(f"使用CPU模式执行裁剪命令: {cmd_str}")
                        
                        # 隐藏窗口
                        startupinfo = None
                        if platform.system() == "Windows":
                            startupinfo = subprocess.STARTUPINFO()
                            startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
                            startupinfo.wShowWindow = subprocess.SW_HIDE
                        
                        self.current_process = subprocess.Popen(cpu_cmd1, stderr=subprocess.PIPE, 
                                                              stdout=subprocess.PIPE, text=True, startupinfo=startupinfo)
                        stdout, stderr = self.current_process.communicate()
                        self.log(f"CPU裁剪命令输出: {stderr}")
                        
                        if self.current_process.returncode != 0:
                            raise subprocess.CalledProcessError(self.current_process.returncode, cpu_cmd1, stderr)
                        
                        success_msg = "使用CPU模式裁剪成功"
                        self.processing_file.emit(success_msg)
                        self.log(success_msg)
                    else:
                        # 如果已经是CPU模式还失败，则抛出异常
                        raise
                
                # 步骤2: 合并生成3D视频
                try:
                    temp_file_escaped = temp_file.replace("\\", "\\\\").replace(":", "\\:")
                    if self.add_black_line:
                        vf_command = (
                            f"movie='{temp_file_escaped}' [in1]; "
                            f"[in]pad=iw*2+{self.black_line_width}:ih:iw+{self.black_line_width}:0[middle]; "
                            f"[middle]drawbox=x=iw/2:y=0:w={self.black_line_width}:h=ih:color=black:t=fill[in0]; "
                            f"[in0][in1] overlay=0:0 [out]"
                        )
                    else:
                        vf_command = (
                            f"movie='{temp_file_escaped}' [in1]; "
                            f"[in]pad=iw*2:ih:iw:0[in0]; "
                            f"[in0][in1] overlay=0:0 [out]"
                        )
                    
                    # 修改命令构建方式，确保硬件加速参数位于输入文件之前
                    cmd2 = []
                    
                    # 添加硬件加速参数（在输入文件之前）
                    if self.hw_accel == "nvidia":
                        cmd2.extend([self.ffmpeg_path, "-hwaccel", "cuda", "-hwaccel_output_format", "cuda"])
                    elif self.hw_accel == "amd":
                        cmd2.extend([self.ffmpeg_path, "-hwaccel", "d3d11va"])
                    elif self.hw_accel == "qsv":
                        cmd2.extend([self.ffmpeg_path, "-hwaccel", "qsv", "-hwaccel_output_format", "qsv"])
                    else:  # cpu
                        cmd2.extend([self.ffmpeg_path])
                    
                    # 添加输入文件
                    cmd2.extend(["-i", processing_input])
                    
                    # 添加视频滤镜和编码器参数
                    if self.hw_accel == "nvidia":
                        cmd2.extend(["-vf", vf_command, "-c:v", "h264_nvenc", "-preset", "medium"])
                    elif self.hw_accel == "amd":
                        # AMD编码器不支持medium预设，移除预设参数
                        cmd2.extend(["-vf", vf_command, "-c:v", "h264_amf"])
                    elif self.hw_accel == "qsv":
                        cmd2.extend(["-vf", vf_command, "-c:v", "h264_qsv", "-preset", "medium"])
                    else:  # cpu
                        cmd2.extend(["-vf", vf_command, "-c:v", "libx264", "-preset", "medium"])
                    
                    # 添加其他通用参数
                    cmd2.extend([
                        "-b:v", "1200k",
                        "-r:v", "25",
                        "-f", "mp4",
                        "-y",
                        output_file
                    ])
                    
                    # 输出命令行
                    cmd_str = " ".join(cmd2)
                    self.log(f"执行3D合成命令: {cmd_str}")
                    self.processing_file.emit(f"执行3D合成命令...")
                    
                    # 隐藏窗口
                    startupinfo = None
                    if platform.system() == "Windows":
                        startupinfo = subprocess.STARTUPINFO()
                        startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
                        startupinfo.wShowWindow = subprocess.SW_HIDE
                    
                    self.current_process = subprocess.Popen(cmd2, stderr=subprocess.PIPE, 
                                                          stdout=subprocess.PIPE, text=True, startupinfo=startupinfo)
                    stdout, stderr = self.current_process.communicate()
                    self.log(f"3D合成命令输出: {stderr}")
                    
                    if self.current_process.returncode != 0:
                        raise subprocess.CalledProcessError(self.current_process.returncode, cmd2, stderr)
                except Exception as e:
                    # 如果硬件加速模式失败，尝试使用CPU模式
                    if self.hw_accel != "cpu":
                        cpu_msg = "硬件加速3D合成失败，尝试使用CPU模式..."
                        self.processing_file.emit(cpu_msg)
                        self.log(cpu_msg)
                        hw_accel_failed = True
                        
                        try:
                            # 使用CPU模式重试
                            cpu_cmd2 = [
                                self.ffmpeg_path,
                                "-i", processing_input,
                                "-vf", vf_command,
                                "-c:v", "libx264",
                                "-preset", "medium",
                                "-b:v", "1200k",
                                "-r:v", "25",
                                "-f", "mp4",
                                "-y",
                                output_file
                            ]
                            
                            cmd_str = " ".join(cpu_cmd2)
                            self.log(f"使用CPU模式执行3D合成命令: {cmd_str}")
                            
                            # 隐藏窗口
                            startupinfo = None
                            if platform.system() == "Windows":
                                startupinfo = subprocess.STARTUPINFO()
                                startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
                                startupinfo.wShowWindow = subprocess.SW_HIDE
                            
                            self.current_process = subprocess.Popen(cpu_cmd2, stderr=subprocess.PIPE, 
                                                                  stdout=subprocess.PIPE, text=True, startupinfo=startupinfo)
                            stdout, stderr = self.current_process.communicate()
                            self.log(f"CPU 3D合成命令输出: {stderr}")
                            
                            if self.current_process.returncode != 0:
                                raise subprocess.CalledProcessError(self.current_process.returncode, cpu_cmd2, stderr)
                            
                            success_msg = "使用CPU模式3D合成成功"
                            self.processing_file.emit(success_msg)
                            self.log(success_msg)
                        except Exception as cpu_e:
                            # 如果CPU模式也失败，则抛出异常
                            error_msg = f"3D合成失败 (CPU模式): {str(cpu_e)}"
                            self.log(error_msg)
                            raise Exception(error_msg)
                    else:
                        # 如果已经是CPU模式还失败，则抛出异常
                        raise
                
                # 清理临时文件
                for temp in [temp_file, watermarked_file]:
                    if os.path.exists(temp):
                        try:
                            os.remove(temp)
                            self.log(f"已删除临时文件: {temp}")
                        except Exception as e:
                            self.log(f"无法删除临时文件 {temp}: {str(e)}")
                
                self.progress_update.emit(i + 1, total_files)
                
            except subprocess.CalledProcessError as e:
                error_msg = f"处理文件 {input_file} 时 FFmpeg 失败: {e.stderr}"
                self.log(error_msg)
                self.error_occurred.emit(error_msg)
            except Exception as e:
                error_msg = f"处理文件 {input_file} 时出错: {str(e)}"
                self.log(error_msg)
                self.error_occurred.emit(error_msg)
        
        # 清理水印临时文件
        if self.add_watermark and self.watermark_path and os.path.exists(self.watermark_path):
            try:
                os.remove(self.watermark_path)
                self.log(f"已删除水印临时文件: {self.watermark_path}")
            except Exception as e:
                self.log(f"无法删除水印临时文件: {str(e)}")
                
        # 确保在所有情况下都发送完成信号
        self.finished_processing.emit()
    
    def stop(self):
        self.is_running = False
        if self.current_process and self.current_process.poll() is None:
            if platform.system() == "Windows":
                # 使用隐藏窗口方式终止进程
                startupinfo = subprocess.STARTUPINFO()
                startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
                startupinfo.wShowWindow = subprocess.SW_HIDE
                
                subprocess.run(["taskkill", "/F", "/T", "/PID", str(self.current_process.pid)], 
                              stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
                              startupinfo=startupinfo)
            else:
                self.current_process.terminate()
                try:
                    self.current_process.wait(timeout=3)
                except subprocess.TimeoutExpired:
                    self.current_process.kill()